home *** CD-ROM | disk | FTP | other *** search
/ ftp.mactech.com 2010 / ftp.mactech.com.tar / ftp.mactech.com / challenge / 13.07 / Challenge.sit / Challenge, Polygon Projection / Challenge.p < prev    next >
Text File  |  1997-05-07  |  16KB  |  485 lines

  1. unit Challenge;
  2.  
  3. (*
  4.  
  5.     Author: A.C.C. Murphy
  6.  
  7.     Assumptions:
  8.         Storage space must be big enough for 13 floats per polygon
  9.         All points must be significantly smaller in magnitude than BIG_FLOAT = 1000000.0
  10.         Polygons are translucent (their colour based uplon lighting is independent of the side of the 
  11.             polygon that is lit)
  12.         50% attenuation of colour is used
  13.         50% attenuation of black is black
  14.         
  15.     Method:
  16.         
  17.         InitProjection is not used
  18.         
  19.         First we precalculate a small bounding sphere for the polygon points.
  20.         Next we get the information about the GWorld to allow direct pixel access.
  21.         Then for each point on the GWorld, we trace the ray from the point to the eye,
  22.             intersecting it with each polygon and finding the one closes to the eye 
  23.             (furthest forward, since the eye is infront of all polygons).  That determines 
  24.             the colour.  We then trace the ray from that intersection point to the light source
  25.             to determine whether the point is in shadow, and if so we halve the intensity.
  26.             We set the colour of the pixel and move on.
  27.     
  28.     Optimizations:
  29.         Direct pixel access to the GWorld (known to be 32 bit)
  30.         Bounding sphere used to optimize the ray/polygon intersection test.
  31.         Time is approximately 2 microseconds per pixel per polygon on an 8500.
  32. *)
  33.  
  34. interface
  35.  
  36.     uses
  37.         Types, Quickdraw, QDOffscreen;
  38.         
  39.     const
  40.         kMAXPOINTS = 10;
  41.  
  42.     const
  43.         BIG_FLOAT = 1000000.0;
  44.             
  45.     type
  46.         float = real;
  47.         
  48.     type
  49.         My2DPoint = record (* point in z==0 plane*)
  50.             x2D: float; (* x coordinate*)
  51.             y2D: float; (* y coordinate*)
  52.         end;
  53.         My3DPoint = record
  54.             x3D: float;                 (* x coordinate*)
  55.             y3D: float;                 (* y coordinate*)
  56.             z3D: float;                 (* z coordinate*)
  57.         end;
  58.         My3DDirection = record
  59.             thetaX:float;              (* angle in radians*)
  60.             thetaY:float;              (* angle in radians*)
  61.             thetaZ:float;              (* angle in radians*)
  62.         end;
  63.         MyPlane = record
  64.             planeNormal: My3DDirection;      (* normal vector to plane*)
  65.             planeOrigin: My3DPoint;    (* origin of plane in 3D space*)
  66.         end;
  67.         MyPolygon = record
  68.             numPoints: longint;             (* number of points in polygon*)
  69.             thePoint: array[0..kMAXPOINTS-1] of My2DPoint;     (* polygon in z==0 plane*)
  70.             polyPlane: MyPlane;             (* rotate/translate z==0 plane to this plane*)
  71.             polyColor: RGBColor;         (* the color to draw this polygon*)
  72.         end;
  73.         MyPolygonArray = array[0..0] of MyPolygon;
  74.         
  75.             
  76.     procedure InitProjection(
  77.         const viewPoint: My3DPoint;         (* viewpoint from which to project*)
  78.         const illumPoint:My3DPoint;         (* viewpoint from which to draw shadow*)
  79.         storage: univ Ptr;                         (* auxiliary storage preallocated for your use*)
  80.         storageSize: longint                     (* number of bytes of storage*)
  81.     );
  82.  
  83.     procedure CalcProjection(
  84.         offScreen: GWorldPtr;                                 (* GWorld to draw projection *)
  85.         const thePolys: MyPolygonArray;         (* polygons to project *)
  86.         numPolys: longint;                                         (* number of polygons to project *)
  87.         const viewPoint: My3DPoint;                             (* viewpoint from which to project *)
  88.         const illumPoint: My3DPoint;                             (* illumination point from which to draw shadow *)
  89.         storage: univ Ptr;                         (* auxiliary storage preallocated for your use*)
  90.         storageSize: longint                     (* number of bytes of storage*)
  91.     );
  92.  
  93. implementation
  94.  
  95.     type
  96.         Ray3D = record
  97.             origin: My3DPoint;
  98.             direction: My3DPoint;
  99.         end;
  100.         PolygonExtra = record
  101.             normal, rotX, rotY, center: My3DPoint;
  102.             radius2: float;
  103.         end;
  104.         PolygonExtraArray = array[0..0] of PolygonExtra;
  105.         StorageRecord = record
  106.             poly_extra: PolygonExtraArray; { must be at the end, since it's an extensible array }
  107.         end;
  108.         StorageRecordPtr = ^StorageRecord;
  109.         
  110.     function DotProduct(const src1, src2 : My3DPoint) : float;
  111.     begin
  112.         DotProduct := src1.x3D*src2.x3D +  src1.y3D*src2.y3D +  src1.z3D*src2.z3D;
  113.     end;
  114.     
  115.     procedure CrossProduct(src1, src2 : My3DPoint; var dst : My3DPoint);
  116.     begin
  117.         dst.x3D := src1.y3D*src2.z3D - src1.z3D*src2.y3D;
  118.         dst.y3D := src1.z3D*src2.x3D - src1.x3D*src2.z3D;
  119.         dst.z3D := src1.x3D*src2.y3D - src1.y3D*src2.x3D;
  120.     end;
  121.     
  122.     procedure AddVectors(const src1, src2 : My3DPoint; var dst : My3DPoint);
  123.     begin
  124.         dst.x3D := src1.x3D + src2.x3D;
  125.         dst.y3D := src1.y3D + src2.y3D;
  126.         dst.z3D := src1.z3D + src2.z3D;
  127.     end;
  128.         
  129.     procedure SubtractVectors(const src1, src2 : My3DPoint; var dst : My3DPoint);
  130.     begin
  131.         dst.x3D := src1.x3D - src2.x3D;
  132.         dst.y3D := src1.y3D - src2.y3D;
  133.         dst.z3D := src1.z3D - src2.z3D;
  134.     end;
  135.     
  136.     procedure MidPoint( const src1, src2 : My3DPoint; var dst : My3DPoint);
  137.     begin
  138.         dst.x3D := (src1.x3D + src2.x3D) / 2;
  139.         dst.y3D := (src1.y3D + src2.y3D) / 2;
  140.         dst.z3D := (src1.z3D + src2.z3D) / 2;
  141.     end;
  142.     
  143.     function Distance2( const src1, src2 : My3DPoint) : float;
  144.     begin
  145.         Distance2 := sqr(src1.x3D - src2.x3D) + sqr(src1.y3D - src2.y3D) + sqr(src1.z3D - src2.z3D);
  146.     end;
  147.     
  148.     procedure ScaleVector(const src : My3DPoint; scale : float; var dst : My3DPoint);
  149.     begin
  150.         dst.x3D := src.x3D * scale;
  151.         dst.y3D := src.y3D * scale;
  152.         dst.z3D := src.z3D * scale;
  153.     end;
  154.         
  155.     procedure NormalizeVector(const src : My3DPoint; var dst : My3DPoint);
  156.         var
  157.             length : float;
  158.     begin
  159.         length := sqrt(DotProduct(src,src));    
  160.         dst.x3D := src.x3D / length;
  161.         dst.y3D := src.y3D / length;
  162.         dst.z3D := src.z3D / length;
  163.     end;
  164.     
  165.     procedure MakeViewRay(const eye : My3DPoint; x, y, z: float; var ray : Ray3D);
  166.     begin
  167.         ray.origin.x3D := x;
  168.         ray.origin.y3D := y;
  169.         ray.origin.z3D := z;
  170.         ray.direction.x3D := eye.x3D - x;
  171.         ray.direction.y3D := eye.y3D - y;
  172.         ray.direction.z3D := eye.z3D - z;
  173.         NormalizeVector(ray.direction, ray.direction);
  174.     end;
  175.     
  176.     procedure RotateX(src : My3DPoint; sinA, cosA : float; var dst : My3DPoint);
  177.     begin
  178.         dst.x3D := src.x3D;
  179.         dst.y3D := cosA*src.y3D - sinA*src.z3D;
  180.         dst.z3D := sinA*src.y3D + cosA*src.z3D;
  181.     end;
  182.     
  183.     procedure RotateY( src : My3DPoint; sinA, cosA : float; var dst : My3DPoint);
  184.     begin
  185.         dst.x3D := cosA*src.x3D + sinA*src.z3D;
  186.         dst.y3D := src.y3D;
  187.         dst.z3D := -sinA*src.x3D + cosA*src.z3D;
  188.     end;
  189.     
  190.     procedure RotateZ( src : My3DPoint; sinA, cosA : float; var dst : My3DPoint);
  191.     begin
  192.         dst.x3D := cosA*src.x3D - sinA*src.y3D;
  193.         dst.y3D := sinA*src.x3D + cosA*src.y3D;
  194.         dst.z3D := src.z3D;
  195.     end;
  196.     
  197.     function PointInPlaneInPolygon( const pt: My2DPoint; const poly: MyPolygon ): boolean;
  198.         function Quadrant( const pt: My2DPoint; x, y: float ): longint;
  199.         begin
  200.             if pt.x2D > x then begin
  201.                 if pt.y2D > y then begin
  202.                     Quadrant := 0;
  203.                 end else begin
  204.                     Quadrant := 3;
  205.                 end;
  206.             end else begin
  207.                 if pt.y2D > y then begin
  208.                     Quadrant := 1;
  209.                 end else begin
  210.                     Quadrant := 2;
  211.                 end;
  212.             end;
  213.         end;
  214.         
  215.         function x_intercept( const pt1, pt2: My2DPoint; yy: float ): float;
  216.         begin
  217.             x_intercept := pt2.x2D - ( (pt2.y2D - yy) * ((pt1.x2D - pt2.x2D) / (pt1.y2D - pt2.y2D)) );
  218.         end;
  219.         
  220.         var
  221.             i, angle, quad, next_quad, delta: longint;
  222.             last_vertex, next_vertex: My2DPoint;
  223.     begin
  224.         angle := 0;
  225.         last_vertex := poly.thePoint[poly.numPoints-1];
  226.         quad := Quadrant( last_vertex, pt.x2D, pt.y2D );
  227.         for i := 1 to poly.numPoints do begin
  228.             next_vertex := poly.thePoint[i-1];
  229.             next_quad := Quadrant( next_vertex, pt.x2D, pt.y2D );
  230.             delta := next_quad - quad;
  231.             case delta of
  232.                 3: delta := -1;
  233.                 -3: delta := 1;
  234.                 2, -2: begin
  235.                     if x_intercept( last_vertex, next_vertex, pt.y2D ) > pt.x2D then begin
  236.                         delta := -delta;
  237.                     end;
  238.                 end;
  239.                 otherwise begin
  240.                 end;
  241.             end;
  242.             angle := angle + delta;
  243.             quad := next_quad;
  244.             last_vertex := next_vertex;
  245.         end;
  246.         PointInPlaneInPolygon := (angle = 4) | (angle = -4);
  247.     end;
  248.     
  249.     function Intersect(const ray: Ray3D; const poly: MyPolygon; const poly_extra: PolygonExtra; var distance : float; var intersectionPt: My3DPoint) : boolean;
  250.         var
  251.             tempVector : My3DPoint;
  252.             temp1, temp2 : float;
  253.             intersectionPoint : My3DPoint;
  254.             intersection2D : My2DPoint;
  255.             Ib, Ic, Id: float;
  256.     begin
  257.         Intersect := false;
  258.  
  259.         { intersect ray with sphere }
  260.         SubtractVectors( ray.origin, poly_extra.center, tempVector );
  261.         Ib := 2 * DotProduct( ray.direction, tempVector );
  262.         Ic := DotProduct( tempVector, tempVector ) - poly_extra.radius2;
  263.         Id := sqr(Ib) - 4.0*Ic;
  264.         if Id >= 0 then begin { yes, ray intersects sphere }
  265.             temp1 := DotProduct( poly.polyPlane.planeOrigin, poly_extra.normal ) - DotProduct( poly_extra.normal, ray.origin );
  266.             temp2 := DotProduct( ray.direction, poly_extra.normal );
  267.             if temp2 <> 0 then begin
  268.                 distance := temp1 / temp2;
  269.                 if distance > 0 then begin
  270.                     ScaleVector(ray.direction, distance, intersectionPoint);
  271.                     AddVectors(intersectionPoint, ray.origin, intersectionPoint);
  272.                     
  273.                     if Distance2( intersectionPoint, poly_extra.center ) <= poly_extra.radius2 then begin 
  274.                         { intersection point is whithin sphere.  Find out if it is actually in the polygon }
  275.                         intersectionPt := intersectionPoint;
  276.                         { First translate back to the origin }
  277.                         SubtractVectors(intersectionPoint, poly.polyPlane.planeOrigin, intersectionPoint);
  278.                         intersection2D.x2D := DotProduct( intersectionPoint, poly_extra.rotX );
  279.                         intersection2D.y2D := DotProduct( intersectionPoint, poly_extra.rotY );
  280.                         { Then check if it is whithin the polygon }
  281.                         Intersect := PointInPlaneInPolygon(intersection2D,poly);
  282.                     end;
  283.                 end;
  284.             end;
  285.         end;
  286.     end;
  287.  
  288.     procedure InitProjection(
  289.         const viewPoint: My3DPoint;        (* viewpoint from which to project *)
  290.         const illumPoint:My3DPoint;        (* viewpoint from which to draw shadow *)
  291.         storage: univ Ptr;                        (* auxiliary storage preallocated for your use *)
  292.         storageSize: longint                    (* number of bytes of storage *)
  293.     );
  294.     begin
  295. {$unused( viewPoint, illumPoint, storage, storageSize )}
  296.     end;
  297.  
  298.     procedure PreparsePolygons( my_storage: StorageRecordPtr; numPolys: longint; const thePolys: MyPolygonArray );
  299.         var
  300.             i, j: longint;
  301.             pt: My3DPoint;
  302.             pts: array[1..kMAXPOINTS] of My3DPoint;
  303.             min_x, min_y, min_z, max_x, max_y, max_z: My3DPoint;
  304.             dist_x, dist_y, dist_z, new_radius2: float;
  305.             radius, new_radius, old_to_new: float;
  306.             sinX, cosX, sinY, cosY, sinZ, cosZ: float;
  307.     begin
  308.         for i := 0 to numPolys-1 do begin
  309.             with my_storage^.poly_extra[i], thePolys[i], polyPlane.planeNormal do begin
  310.                 sinX := sin(thetaX);
  311.                 cosX := cos(thetaX);
  312.                 sinY := sin(thetaY);
  313.                 cosY := cos(thetaY);
  314.                 sinZ := sin(thetaZ);
  315.                 cosZ := cos(thetaZ);
  316.                 normal.x3d := sinY*cosX;
  317.                 normal.y3d := -sinX;
  318.                 normal.z3d := cosY*cosX;
  319.                 rotX.x3D := 1;
  320.                 rotX.y3D := 0;
  321.                 rotX.z3D := 0;
  322.                 RotateZ(rotX, sinZ, cosZ, rotX);
  323.                 RotateX(rotX, sinX, cosX, rotX);
  324.                 RotateY(rotX, sinY, cosY, rotX);
  325.                 rotY.x3D := 0;
  326.                 rotY.y3D := 1;
  327.                 rotY.z3D := 0;
  328.                 RotateZ(rotY, sinZ, cosZ, rotY);
  329.                 RotateX(rotY, sinX, cosX, rotY);
  330.                 RotateY(rotY, sinY, cosY, rotY);
  331.                 
  332.                 for j := 1 to numPoints do begin
  333.                     pt.x3D := thePoint[j-1].x2D;
  334.                     pt.y3D := thePoint[j-1].y2D;
  335.                     pt.z3D := 0;
  336.                     RotateZ(pt, sinZ, cosZ, pt);
  337.                     RotateX(pt, sinX, cosX, pt);
  338.                     RotateY(pt, sinY, cosY, pt);
  339.                     pts[j] := pt;
  340.                     if j = 1 then begin
  341.                         min_x := pt; min_y := pt; min_z := pt;
  342.                         max_x := pt; max_y := pt; max_z := pt;
  343.                     end else begin
  344.                         if pt.x3D < min_x.x3D then begin
  345.                             min_x := pt;
  346.                         end;
  347.                         if pt.y3D < min_y.y3D then begin
  348.                             min_y := pt;
  349.                         end;
  350.                         if pt.z3D < min_z.z3D then begin
  351.                             min_z := pt;
  352.                         end;
  353.                         if pt.x3D > max_x.x3D then begin
  354.                             max_x := pt;
  355.                         end;
  356.                         if pt.y3D > max_y.y3D then begin
  357.                             max_y := pt;
  358.                         end;
  359.                         if pt.z3D > max_z.z3D then begin
  360.                             max_z := pt;
  361.                         end;
  362.                     end;
  363.                 end;
  364.                 
  365.                 dist_x := Distance2( min_x, max_x );
  366.                 dist_y := Distance2( min_y, max_y );
  367.                 dist_z := Distance2( min_z, max_z );
  368.                 if dist_x > dist_y then begin
  369.                     if dist_x > dist_z then begin
  370.                         radius2 := dist_x/4;
  371.                         MidPoint( min_x, max_x, center );
  372.                     end else begin
  373.                         radius2 := dist_z/4;
  374.                         MidPoint( min_z, max_z, center );
  375.                     end;
  376.                 end else begin
  377.                     if dist_y > dist_z then begin
  378.                         radius2 := dist_y/4;
  379.                         MidPoint( min_y, max_y, center );
  380.                     end else begin
  381.                         radius2 := dist_z/4;
  382.                         MidPoint( min_z, max_z, center );
  383.                     end;
  384.                 end;
  385.                 
  386.                 for j := 1 to numPoints do begin
  387.                     new_radius2 := Distance2( center, pts[j] );
  388.                     if new_radius2 > radius2 then begin
  389.                         radius := sqrt(radius2);
  390.                         new_radius := sqrt(new_radius2);
  391.                         radius2 := (radius + new_radius)/2;
  392.                         old_to_new := radius2 - radius;
  393.                         center.x3D := (radius2*center.x3D + old_to_new*pts[j].x3D)/radius;
  394.                         center.y3D := (radius2*center.y3D + old_to_new*pts[j].y3D)/radius;
  395.                         center.z3D := (radius2*center.z3D + old_to_new*pts[j].z3D)/radius;
  396.                         radius2 := sqr(radius2);
  397.                     end;
  398.                 end;
  399.             
  400.                 AddVectors( polyPlane.planeOrigin, center, center );
  401.  
  402.             end;
  403.         end;
  404.     end;
  405.     
  406.     procedure CalcProjection(
  407.         offScreen: GWorldPtr;                                (* GWorld to draw projection *)
  408.         const thePolys: MyPolygonArray;        (* polygons to project *)
  409.         numPolys: longint;                                        (* number of polygons to project *)
  410.         const viewPoint: My3DPoint;                            (* viewpoint from which to project *)
  411.         const illumPoint: My3DPoint;                            (* illumination point from which to draw shadow *)
  412.         storage: univ Ptr;                        (* auxiliary storage preallocated for your use *)
  413.         storageSize: longint                    (* number of bytes of storage *)
  414.     );
  415.         var
  416.             bounds: Rect;
  417.             x, y : integer;
  418.             colour : RGBColor;
  419.             viewRay : Ray3D;
  420.             lightRay : Ray3D;
  421.             i : integer;
  422.             closestDistance : float;
  423.             closestIntersectionPt: My3DPoint;
  424.             thisDistance : float;
  425.             intersectionPt: My3DPoint;
  426.             intersect_polygon: longint;
  427.             pm: PixMapHandle;
  428.             junk_boolean: boolean;
  429.             pixels: Ptr;
  430.             rowbytes_add: longint;
  431.             my_storage: StorageRecordPtr;
  432.     begin
  433. {$unused( storage, storageSize )}
  434.         my_storage := StorageRecordPtr(storage);
  435.  
  436.         PreparsePolygons( my_storage, numPolys, thePolys );
  437.  
  438.         SetGWorld( offScreen, nil );
  439.         bounds := offScreen^.PortRect;
  440.         pm := GetGWorldPixMap( offScreen );
  441.         junk_boolean := LockPixels( pm );
  442.         pixels := GetPixBaseAddr( pm );
  443.         rowbytes_add := band( pm^^.rowBytes, $3FFF ) - 4 * (bounds.right - bounds.left);
  444.  
  445.         for y := bounds.top to bounds.bottom-1 do begin
  446.             for x := bounds.left to bounds.right-1 do begin
  447.                 MakeViewRay(viewPoint, x, y, 0, viewRay);
  448.                 closestDistance := 0.0;
  449.                 intersect_polygon := -1;
  450.                 for i:= 1 to numPolys do begin
  451.                     if Intersect(viewRay, thePolys[i-1], my_storage^.poly_extra[i-1], thisDistance, intersectionPt) then begin
  452.                         if (thisDistance > closestDistance) then begin
  453.                             intersect_polygon := i;
  454.                             closestDistance := thisDistance;
  455.                             closestIntersectionPt := intersectionPt;
  456.                         end;
  457.                     end
  458.                 end;
  459.                 if intersect_polygon > 0 then begin
  460.                     colour := thePolys[intersect_polygon-1].polyColor;
  461.  
  462.                     MakeViewRay(illumPoint, closestIntersectionPt.x3D, closestIntersectionPt.y3D, closestIntersectionPt.z3D, lightRay);
  463.  
  464.                     for i:= 1 to numPolys do begin
  465.                         if (intersect_polygon <> i) & Intersect(lightRay, thePolys[i-1], my_storage^.poly_extra[i-1], thisDistance, intersectionPt) then begin
  466.                             colour.red := band(colour.red, $0FFFF) div 2;
  467.                             colour.green := band(colour.green, $0FFFF) div 2;
  468.                             colour.blue := band(colour.blue, $0FFFF) div 2;
  469.                             leave;
  470.                         end
  471.                     end;
  472.                     
  473.                     LongintPtr(pixels)^ := bsl( band(colour.red, $0FF00), 8 ) + 
  474.                             band(colour.green, $0FF00) + bsr( band(colour.blue, $0FF00), 8 );
  475.                 end else begin
  476.                     LongintPtr(pixels)^ := 0;
  477.                 end;
  478.                 longint(pixels) := longint(pixels) + 4;
  479.             end;
  480.             longint(pixels) := longint(pixels) + rowbytes_add;
  481.         end;
  482.     end;
  483.  
  484. end.
  485.